#include <asm-generic/errno-base.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>

#include "relayer.h"

#define TTY1 		"./tty11"
#define TTY2 		"./tty12"
#define BUFFSIZE 	1024

/*
 * 作者：吴开杰
 * 日期：2021/10/08 09:53
 * 描述：
 * 1.实现对两个设备文件，实时数据读写交换
 * 2.采用有限状态机的编程思想
 * */

/*
 * job的读写状态
 * */
enum {
	STATE_R = 1,
	STATE_W,
	STATE_EX,
	STATE_T
};

/*
 * job的运行状态
 * */
enum {
	STATE_RUNNING = 1,
	STATE_CANCENED,
	STATE_OVER,
};

/*
 * 定义job任务-读写数据实体
 * */
struct fsm_st {
	int state;
	int sfd;
	int dfd;
	int len;
	int pos;
	char buf[BUFFSIZE];
	char *erstr;
};

/*
 * 定义job
 * */
struct job_st {
	int job_state;
	int fd1;
	int fd2;
	int fd1_save, fd2_save;
	struct fsm_st fsm12, fsm21;
};

static struct job_st *jobs[JOB_MAX];							// 存放任务
static pthread_mutex_t mutex_job = PTHREAD_MUTEX_INITIALIZER;	// 在添加任务与消耗任务需要添加锁，防止数据读写错误
static pthread_once_t once_job_init = PTHREAD_ONCE_INIT;		// 只创建一个线程，防止多次创建

/*
 * 状态机核心部分-状态交换
 * */
static void fsm_driver(struct fsm_st *fsm)
{
	int ret;
	switch (fsm->state) {
		case STATE_R:
			fsm->len = read(fsm->sfd, fsm->buf, BUFFSIZE);
			printf("read data len: %d \n",fsm->len);
			if(fsm->len == 0){
				// 没有读取到数据，继续读
				fsm->state = STATE_T;
			} else if(fsm->len < 0) {
				// 读取伪错误
				if(errno == EAGAIN){
					fsm->state = STATE_R;
				} else if(errno == EINTR){
					fsm->state = STATE_R;
				} else {
					fsm->erstr = "read() failed";
					fsm->state = STATE_EX;
				}
			} else {
				// 读取到数据
				fsm->pos = 0;
				fsm->state = STATE_W;
			}
			break;
		case STATE_W:
			ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
			printf("write data len: %d \n",ret);
			if(ret < 0){
				// 写入伪错误 
				if(errno == EAGAIN){
					fsm->state = STATE_W;
				} else if(errno == EINTR){
					fsm->state = STATE_W;
				} else {
					fsm->erstr = "write() failed";
					fsm->state = STATE_EX;
				}
			} else {
				fsm->pos += ret; // 记录已经写入的位置
				fsm->len -= ret; // 记录剩余数量量
				if(fsm->len == 0){
					// 写入完成，改变状态去读取
					fsm->state = STATE_R;
				} else {
					// 写入未完成，继续写入
					fsm->state = STATE_W;
				}
			}
			break;
		case STATE_EX:
			perror(fsm->erstr);
			// 这里是继续去读
			fsm->state = STATE_T;
			break;
		case STATE_T:
			printf("job state: STATE_T \n");
			break;
		default:
			printf("job state: default \n");
			abort();
			break;
	}
}

/*
 * 异步线程-处理已经添加到任务列表的job
 * */
static void *task_relayer(void *p)
{
	int i;
	while(1){
		pthread_mutex_lock(&mutex_job);
		for (i = 0; i < JOB_MAX; i++) {
			if(jobs[i] != NULL){
				if(jobs[i]->job_state == STATE_RUNNING){
					fsm_driver(&jobs[i]->fsm12);
					fsm_driver(&jobs[i]->fsm21);
					if(jobs[i]->fsm12.state == STATE_T && jobs[i]->fsm21.state == STATE_T){
						jobs[i]->job_state = STATE_OVER;
					}
				}
			}
		}
		pthread_mutex_unlock(&mutex_job);
	}
}

/*
 * 模块-卸载
 * */
static void module_unload(void)
{

    int i;
    for (i = 0; i < JOB_MAX; ++i) {
        if (jobs[i] == NULL) {
			// 恢复文件属性
			fcntl(jobs[i]->fd1, F_SETFL,jobs[i]->fd1_save);
			fcntl(jobs[i]->fd2, F_SETFL,jobs[i]->fd2_save);

			// 释放空间
			free(jobs[i]);
        }
    }

	// 销毁锁
	pthread_mutex_destroy(&mutex_job);

}

/*
 * 模块-挂载
 * */
static void module_load(void)
{
	pthread_t tid_relayer;
	int err;
    err = pthread_create(&tid_relayer, NULL, task_relayer, NULL);
    if (err) {
        fprintf(stderr, "pthread_create(): %d\n", strerror(err));
        exit(1);
    }
	// 挂载一个钩子函数，去卸载模块
	atexit(module_unload);
}

/*
 * 查看任务列表是否有空位置
 * */
static int get_free_pos_unlocked(void)
{
    int i;
    for (i = 0; i < JOB_MAX; ++i) {
        if (jobs[i] == NULL) {
            return i;
        }
    }
    return -1;
}

/*
 * 添加job
 * */
int job_add(int fd1, int fd2)
{

	int pos;
	struct job_st *job;
	
	// 首次加载需要去初始化模块，创建工作线程
	pthread_once(&once_job_init,module_load);

	job = malloc(sizeof(*job));
	if(job == NULL){
		return -ENOMEM;
	}

	job->fd1 = fd1;
	job->fd2 = fd2;
	job->job_state = STATE_RUNNING;
	job->fd1_save = fcntl(job->fd1, F_GETFL);
	fcntl(job->fd1, F_SETFL, job->fd1_save|O_NONBLOCK);	// 设置文件属性
	job->fd2_save = fcntl(job->fd2, F_GETFL);
	fcntl(job->fd2, F_SETFL, job->fd2_save|O_NONBLOCK);	// 设置文件属性

	job->fsm12.sfd = job->fd1;
	job->fsm12.dfd = job->fd2;
	job->fsm12.state = STATE_R;

	job->fsm21.sfd = job->fd2;
	job->fsm21.dfd = job->fd1;
	job->fsm21.state = STATE_R;

	pthread_mutex_lock(&mutex_job);
	pos = get_free_pos_unlocked();
	if(pos < 0){
		pthread_mutex_unlock(&mutex_job);			// 释放锁
		fcntl(job->fd1, F_SETFL, job->fd1_save);	// 恢复fd1文件属性
		fcntl(job->fd2, F_SETFL, job->fd2_save);	// 恢复fd2文件属性
		free(job);									// 释放内存
		return -ENOSPC;
	}
	jobs[pos] = job;
	pthread_mutex_unlock(&mutex_job);
	
	return pos;
}

#if 0

int job_cancel(int fd)
{

}

int job_wait(int fd, job_state_t *state)
{

}

int job_state(int fd, job_state_t *state)
{

}

#endif

